package com.agileboot.common.utils.poi;

import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode.Internal;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * CSV文件处理工具类
 *
 * @author agileboot
 */
@Slf4j
public class CsvUtil {

    private static final String CSV_DELIMITER = ",";
    private static final String LINE_BREAK = "\r\n";
    private static final char QUOTE_CHAR = '"';

    private CsvUtil() {
    }

    /**
     * 写入CSV数据到响应对象
     *
     * @param list     数据列表
     * @param clazz    数据类型
     * @param response 响应对象
     * @param <T>      数据类型
     */
    public static <T> void writeToResponse(List<T> list, Class<T> clazz, HttpServletResponse response) {
        try {
            writeToOutputStream(list, clazz, response.getOutputStream());
        } catch (IOException e) {
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }

    /**
     * 将数据写入输出流
     *
     * @param list         数据列表
     * @param clazz        数据类型
     * @param outputStream 输出流
     * @param <T>          数据类型
     */
    public static <T> void writeToOutputStream(List<T> list, Class<T> clazz, OutputStream outputStream) {
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
            // 获取所有字段
            Field[] fields = clazz.getDeclaredFields();
            List<Field> exportFields = new ArrayList<>();
            Map<String, String> fieldAliasMap = new HashMap<>();

            // 处理注解
            for (Field field : fields) {
                field.setAccessible(true);
                ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
                if (annotation != null) {
                    exportFields.add(field);
                    fieldAliasMap.put(field.getName(), annotation.name());
                }
            }

            // 写入表头
            StringBuilder headerBuilder = new StringBuilder();
            for (int i = 0; i < exportFields.size(); i++) {
                Field field = exportFields.get(i);
                String headerName = fieldAliasMap.get(field.getName());
                headerBuilder.append(escapeCSVValue(headerName));
                if (i < exportFields.size() - 1) {
                    headerBuilder.append(CSV_DELIMITER);
                }
            }
            writer.write(headerBuilder.toString());
            writer.write(LINE_BREAK);

            // 写入数据
            for (T item : list) {
                StringBuilder lineBuilder = new StringBuilder();
                for (int i = 0; i < exportFields.size(); i++) {
                    Field field = exportFields.get(i);
                    Object value = field.get(item);
                    lineBuilder.append(escapeCSVValue(value == null ? "" : value.toString()));
                    if (i < exportFields.size() - 1) {
                        lineBuilder.append(CSV_DELIMITER);
                    }
                }
                writer.write(lineBuilder.toString());
                writer.write(LINE_BREAK);
            }

            writer.flush();
        } catch (Exception e) {
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }

    /**
     * 从请求中读取CSV文件
     *
     * @param clazz 目标类型
     * @param file  MultipartFile文件
     * @param <T>   数据类型
     * @return 解析后的数据列表
     */
    public static <T> List<T> readFromRequest(Class<T> clazz, MultipartFile file) {
        try {
            return readFromInputStream(clazz, file.getInputStream());
        } catch (IOException e) {
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }
    }

    /**
     * 从输入流读取CSV数据
     *
     * @param clazz       目标类型
     * @param inputStream 输入流
     * @param <T>         数据类型
     * @return 解析后的数据列表
     */
    public static <T> List<T> readFromInputStream(Class<T> clazz, InputStream inputStream) {
        List<T> resultList = new ArrayList<>();
        Map<String, String> headerMap = new HashMap<>();
        Map<String, Field> fieldMap = new HashMap<>();

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            // 处理表头
            String headerLine = reader.readLine();
            if (headerLine == null) {
                return resultList;
            }

            List<String> headers = parseCSVLine(headerLine);
            Field[] fields = clazz.getDeclaredFields();

            // 初始化字段映射
            for (Field field : fields) {
                field.setAccessible(true);
                ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
                if (annotation != null) {
                    fieldMap.put(annotation.name(), field);
                }
            }

            // 将表头与字段关联
            for (int i = 0; i < headers.size(); i++) {
                String header = headers.get(i);
                if (fieldMap.containsKey(header)) {
                    headerMap.put(String.valueOf(i), header);
                }
            }

            // 读取数据行
            String line;
            while ((line = reader.readLine()) != null) {
                if (StringUtils.isBlank(line)) {
                    continue;
                }

                List<String> values = parseCSVLine(line);
                T instance = clazz.newInstance();

                for (int i = 0; i < values.size(); i++) {
                    String headerKey = String.valueOf(i);
                    if (headerMap.containsKey(headerKey)) {
                        String headerName = headerMap.get(headerKey);
                        Field field = fieldMap.get(headerName);
                        String value = values.get(i);

                        if (field != null && value != null) {
                            setFieldValue(instance, field, value);
                        }
                    }
                }

                resultList.add(instance);
            }
        } catch (Exception e) {
            throw new ApiException(e, Internal.EXCEL_PROCESS_ERROR, e.getMessage());
        }

        return resultList;
    }

    /**
     * 设置字段值
     *
     * @param instance 实例对象
     * @param field    字段
     * @param value    值
     */
    private static void setFieldValue(Object instance, Field field, String value) {
        try {
            Class<?> fieldType = field.getType();
            Object convertedValue;

            if (String.class.equals(fieldType)) {
                convertedValue = value;
            } else if (Integer.class.equals(fieldType) || int.class.equals(fieldType)) {
                convertedValue = StringUtils.isBlank(value) ? 0 : Integer.parseInt(value);
            } else if (Long.class.equals(fieldType) || long.class.equals(fieldType)) {
                convertedValue = StringUtils.isBlank(value) ? 0L : Long.parseLong(value);
            } else if (Double.class.equals(fieldType) || double.class.equals(fieldType)) {
                convertedValue = StringUtils.isBlank(value) ? 0.0 : Double.parseDouble(value);
            } else if (Boolean.class.equals(fieldType) || boolean.class.equals(fieldType)) {
                convertedValue = "true".equalsIgnoreCase(value) || "1".equals(value);
            } else {
                convertedValue = value;
            }

            field.set(instance, convertedValue);
        } catch (Exception e) {
            log.warn("设置字段值失败: {}", e.getMessage());
            // 继续处理其他字段，不抛出异常
        }
    }

    /**
     * 解析CSV行
     *
     * @param line CSV行
     * @return 字段值列表
     */
    private static List<String> parseCSVLine(String line) {
        List<String> result = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        boolean inQuotes = false;

        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            if (c == QUOTE_CHAR) {
                // 处理引号
                if (inQuotes && i + 1 < line.length() && line.charAt(i + 1) == QUOTE_CHAR) {
                    // 双引号转义为单引号
                    sb.append(QUOTE_CHAR);
                    i++;
                } else {
                    // 切换引号状态
                    inQuotes = !inQuotes;
                }
            } else if (c == ',' && !inQuotes) {
                // 字段分隔符
                result.add(sb.toString());
                sb.setLength(0);
            } else {
                // 普通字符
                sb.append(c);
            }
        }
        result.add(sb.toString());
        return result;
    }

    /**
     * 转义CSV值
     *
     * @param value 原始值
     * @return 转义后的值
     */
    private static String escapeCSVValue(String value) {
        if (value == null) {
            return "";
        }

        if (value.contains("\"") || value.contains(",") || value.contains("\n") || value.contains("\r")) {
            return "\"" + value.replace("\"", "\"\"") + "\"";
        }
        return value;
    }
} 